<?php

namespace helper;

/*
-- ----------------------------
-- auth_rule，规则表
-- ----------------------------
DROP TABLE IF EXISTS `auth_rule`;
CREATE TABLE `auth_rule` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
    `pid` int(11)  NOT NULL DEFAULT '0' COMMENT '父节点',
    `name` varchar(255) NOT NULL DEFAULT '' COMMENT '规则唯一标识',
    `title` varchar(255) NOT NULL DEFAULT '' COMMENT '规则中文名称',
    `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态：为1正常，为0禁用',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- auth_group 用户组表
-- ----------------------------
DROP TABLE IF EXISTS `auth_group`;
CREATE TABLE `auth_group` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '规则唯一标识',
    `title` varchar(255) NOT NULL DEFAULT '' COMMENT '用户组中文名称',
    `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态：为1正常，为0禁用',
    `rules` varchar(3000) NOT NULL DEFAULT '' COMMENT '用户组拥有的规则id， 多个规则","隔开',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;
 */

use think\facade\Db;
use think\facade\Session;

class Auth
{
  //对象实例
  private static $instance;
  //默认配置
  protected $_config = array(
      'auth_on' => true, //认证开关
      'auth_type' => 1, // 认证方式，1为实时认证；2为登录认证
      'auth_group' => 'auth_group', //用户组数据表名
      'auth_rule' => 'auth_rule', //权限规则表
      'auth_user' => 'admin', //用户表
      'administrator' => ['1'] //超级管理员用户组
  );

  /**
   * 初始化配置信息
   * AuthUtil constructor.
   */
  public function __construct()
  {
    if (config('?auth')) {
      $this->_config = array_merge($this->_config, config('auth'));
    }
  }


  /**
   * 实例化接口
   * @return static
   */
  public static function instance()
  {
    if (is_null(self::$instance)) {
      self::$instance = new static();
    }
    return self::$instance;
  }

  /*·
   * 检测权限
   * @param $name //可以是字符串或数组或逗号分割
   * @param $uid //用户ID
   * @param string $mode 执行check的模式 1:url模式 2:功能模式
   * @param string $relation //$or 是否为or关系，为true时name为数组，只要数组中有一个条件通过则通过，如果为false需要全部条件通过。
   * @return bool
   */
  public function check($name, $uid, $mode = 'url', $relation = 'or')
  {
    if (!$this->_config['auth_on']) return true;
    if (in_array($uid, $this->_config['administrator'])) return true; //超级管理员
    $authList = $this->getAuthList($uid);
    if (is_string($name)) {
      $name = strtolower($name);
      if (strpos($name, ',') !== false) {
        $name = explode(',', $name);
      } else {
        $name = array($name);
      }
    }
    $list    = []; //保存验证通过的规则名
    $request = [];
    if ('url' == $mode) $request = unserialize(strtolower(serialize(request()->param())));
    foreach ($authList as $auth) {
      $query = preg_replace('/^.+\?/U', '', $auth['name']);
      if ('url' == $mode && $query != $auth['name']) {
        parse_str($query, $param); //解析规则中的param
        $intersect    = array_intersect_assoc($request, $param);
        $auth['name'] = preg_replace('/\?.*$/U', '', $auth['name']);
        if (in_array($auth['name'], $name) && $intersect == $param) {
          $list[] = $auth['name']; //如果节点相符且url参数满足
        }
      } else {
        if (in_array($auth['name'], $name)) {
          $list[] = $auth['name'];
        }
      }
    }
    if ($relation == 'or' and !empty($list)) return true;
    $diff = array_diff($name, $list);
    if ($relation == 'and' and empty($diff)) return true;
    return false;
  }

  /*
   * 获取用户拥有权限列表
   * @param $groupId
   * @return array|mixed
   */
  public function getAuthList($uid)
  {
    static $_authList = []; //保存用户验证通过的权限列表
    if (isset($_authList[$uid])) return $_authList[$uid];
    if (2 == $this->_config['auth_type'] && Session::has('_auth_list_' . $uid)) {
      return Session::get('_auth_list_' . $uid);
    }
    $map[] = ['status', '=', 1];
    if (!in_array($uid, $this->_config['administrator'])){
      $groupId = Db::table($this->_config['auth_user'])->cache(60)->where('id',$uid)->value('group_id');
      $map[] = ['id', '=', $groupId];
    }
    $ruleIds = Db::table($this->_config['auth_group'])->where($map)->column('rules');
    if (empty($ruleIds)) return [];
    $rules = Db::table($this->_config['auth_rule'])
        ->where('status', 1)
        ->where('id', 'in', implode(',', $ruleIds))
        ->select()
        ->toArray();
    $_authList[$uid] = $rules;
    if (2 == $this->_config['auth_type']) Session::set('_auth_list_' . $uid, $rules);
    return $rules;
  }

}